#pragma once

#include "SequenceView.h"
#include "FramerateCalculator.h"
#include "Types.h"
#include <mswin/synchronizable_thread.h>
#include <boost/shared_ptr.hpp>

namespace My {

template <typename T>
class AsyncSequenceView : 
	public SequenceView,
	protected mswin::synchronizable_thread
{
public:
	typedef T PaintingKey;
	typedef boost::shared_ptr<PaintingKey> PaintingKeySPtr;
	struct Buffer {
		PaintingKeySPtr Key; ///< Key->equal(*Image->id) is always the case, but *Key may contain more information than *Image->id
		CachedImageSPtr	Image; ///< Image from the cache
	};
	typedef boost::shared_ptr<Buffer> BufferSPtr; // we need shared_ptr since it contains locks inside that guaranty atomicity of change
public:
	AsyncSequenceView();
	virtual ~AsyncSequenceView();
public:
	using mswin::synchronizable_thread::idle_or_wnd_msg;
	using mswin::synchronizable_thread::wait_idle_with_msg_processing;
	virtual void HandleKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
public:
	virtual PaintingKeySPtr GetPaintingKey(Size Frame) = 0;
	/// returned image will contain requested information (image will either be taken from cache or painted), 
	/// but its id may differ from p;
	/// if image is painted during the call and RamPreview is true, it will be placed in cache
	virtual CachedImageSPtr GetPainting(PaintingKeySPtr const & p, icache::nearby may_be_requested_later) = 0;
protected:
	virtual afx_msg void OnTimer(UINT nIDEvent);
	virtual TimeControls * GetTimeControls()
		{ return 0; }
protected: //SequenceView interface
	virtual void PaintImageRect(CDC * pDC, CRect const & rClient, CRect const & rImage);
	virtual void InvalidateBuffer();
	virtual void EraseBuffer();
protected:
	bool IsBufferEmpty() const
		{ return !m_Buffer; }
	/// if the buffer is not empty calculates frame rate and mark it as once displayed
	BufferSPtr GetBufferForDisplaying();
	/// returns description of buffer contents
	PaintingKeySPtr GetBufferKey()
		{ return m_Buffer ? m_Buffer->Key : PaintingKeySPtr(); }
	PaintingKeySPtr GetCurrentPaintingKey();
private:
	/// Calculated rate of displaying of new images in view
	void CalcRealFramerate();
private: //mswin::synchronizable_thread interface
	/// Painting take place here
	virtual void report_error(const char * msg);
	virtual void doit();
private:
	BufferSPtr			m_Buffer;
	bool				m_BufferOnceDisplayed; ///< is cleared in painter when it prepares new image; is set in PaintImageRect()
	bool				m_PainterWasNotInTime; ///< working thread was not in time preparing next frame
	FramerateCalculator	m_FPS;
	/// Order prepared for painting thread or currently executed by it or executed last time and available in buffer;
	/// m_Order may be changed during painting, that means cancelation of previous order
	PaintingKeySPtr		m_Order; ///< painting thread don't touches it
};

} //namespace My
